#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File        :Project_CAN_TC.py
@Desc        :同星CAN
@Time        :2022/06/17 16:42:11
@Author        :yangliang
'''
from ast import If, Return
import binascii
import datetime
from email.headerregistry import Address
from email.message import Message
from gettext import npgettext
from pickle import TRUE
import re
import string
import struct
import time
from tracemalloc import start
from pytest_assume.plugin import assume
import sys
import os
import logging
import allure
from sympy import NotReversible

import conftest
curPath = os.path.abspath(os.path.dirname(__file__))
rootPath = os.path.abspath(os.path.dirname(curPath) + os.path.sep + ".")
sys.path.append(rootPath)
sys.path.append(os.path.join(curPath,'commDriver'))
sys.path.append(os.path.join(curPath,'data'))
logging.info(sys.path)

from functools import reduce
from commDriver.TS_CAN import TSCAN, TSCAN_UDS,HIL_Obj
from commDriver.TS_CAN import TSMasterAPI
import Set_up
from commDriver.modbus_plc import *
from commDriver.power import power
import numpy as np
from commDriver.zlg.zlgcan import *
from commDriver.zlg.zuds_test_main import *

import crcmod.predefined
from aes_cmac import AESCMAC 



# 注册接收回调
def my_receive_callback(project_rbs):
    # 使用字典映射来简化条件判断,提高可读性和可维护性
    response_map = {
        0x01: [0x02, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d],
        0x02: [0x03, 0x0F, 0x08, 0x09, 0x00, 0x00, 0x00, 0x00],
        0x03: [0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]
    }

    def callback_wrapper(frame_id, data):
        # 使用字典查找替代多重if-else
        if data and data[0] in response_map:
            project_rbs.update_signal_data(frame_id, response_map[data[0]])
        # 可以添加默认处理逻辑或日志记录

    return callback_wrapper

'''计算CRC值'''
def CalCRC8(data):
    polynomial = 0x1D
    initValue = 0x00
    xorValue = 0x00
    for i in range(len(data)):
        initValue = initValue ^ data[i]
        for j in range(8):
            if(initValue & 0x80):
                initValue = ((initValue << 1) & 0xff)
                initValue = initValue ^ polynomial
            else:
                initValue = ((initValue << 1) & 0xff)
    initValue = initValue ^ xorValue
    return initValue



Enable_0x1C2_RollingCounter_Error = True
Enable_0x1C2_Checksum_Error = True

# 回调函数,监控收到某帧报文后作何种操作
def On_CANFD_EVENT_Pre(OBJ, ACAN):

    global Enable_0x1C2_Checksum_Error
    global Enable_0x1C2_RollingCounter_Error

    # if ((ACAN.contents.FIdentifier == 0x1C2) and (ACAN.contents.FIdxChn == 0)):
    #     # RollingCounter
    #     if Enable_0x1C2_RollingCounter_Error != True:
    #         a = ACAN.contents.FData[1] & 0x0F # FData 字节数组int类型
    #         a += 1
    #         if a > 15:
    #             a = 0
    #         ACAN.contents.FData[1] = (a & 0x0F) | (ACAN.contents.FData[1] & 0xF0)
    #     else:
    #         ACAN.contents.FData[1] = 0 | (ACAN.contents.FData[1] & 0xF0)

    #     # Checksum
    #     if Enable_0x1C2_Checksum_Error != True:
    #         ACAN.contents.FData[0] = CalCRC8(ACAN.contents.FData[1:])
    #     else:
    #         ACAN.contents.FData[0] = 0

    # if ACAN.contents.FIdentifier == 0x0BA and ACAN.contents.FIdxChn == 0:
    #     if Set_up.vinAes != []:
    #         ACAN.contents.FData[0:8] = Set_up.vinAes[0:8]
    
    if ACAN.contents.FIdxChn == 0 and ACAN.contents.FIdentifier == 0x5E2:
        ACAN.contents.FData[0] = 0x19

    # 检查是否是目标流控帧
    if ACAN.contents.FData[0] == 0x30 and (ACAN.contents.FIdentifier == 0x750 or ACAN.contents.FIdentifier == 0x7DF):
        if Set_up.is_block_flow_control:
            # 尝试拦截发送
            try:
                conftest.__Driver['CAN'].block_current_pretx()
                # logging.info('pre: 成功调用 block_current_pretx() 拦截发送')
            except Exception as e:
                logging.error(f'pre:调用 block_current_pretx() 失败: {str(e)}')


def crc16_table(bytestr):
    '''
    crc16查表法
    :param bytestr: bytes字符串
    :return: int16类型
    '''
    crc = 0xFFFF
    data = bytearray(bytestr)
    len1 = len(bytestr)
    # logging.error('len:[{0}]'.format(len1))
    for i in range(2,len1):
        cp = (crc >> 8) ^ data[i]
        crc = ((crc << 8) & 0xFFFF) ^ mCRC16_Tables[cp]
    return crc

# CRC16 余式0x1021表
mCRC16_Tables=[ 0x0000,0x1021,0x2042,0x3063,0x4084,0x50A5,0x60C6,0x70E7,
                0x8108,0x9129,0xA14A,0xB16B,0xC18C,0xD1AD,0xE1CE,0xF1EF,
                0x1231,0x0210,0x3273,0x2252,0x52B5,0x4294,0x72F7,0x62D6,
                0x9339,0x8318,0xB37B,0xA35A,0xD3BD,0xC39C,0xF3FF,0xE3DE,
                0x2462,0x3443,0x0420,0x1401,0x64E6,0x74C7,0x44A4,0x5485,
                0xA56A,0xB54B,0x8528,0x9509,0xE5EE,0xF5CF,0xC5AC,0xD58D,
                0x3653,0x2672,0x1611,0x0630,0x76D7,0x66F6,0x5695,0x46B4,
                0xB75B,0xA77A,0x9719,0x8738,0xF7DF,0xE7FE,0xD79D,0xC7BC,
                0x48C4,0x58E5,0x6886,0x78A7,0x0840,0x1861,0x2802,0x3823,
                0xC9CC,0xD9ED,0xE98E,0xF9AF,0x8948,0x9969,0xA90A,0xB92B,
                0x5AF5,0x4AD4,0x7AB7,0x6A96,0x1A71,0x0A50,0x3A33,0x2A12,
                0xDBFD,0xCBDC,0xFBBF,0xEB9E,0x9B79,0x8B58,0xBB3B,0xAB1A,
                0x6CA6,0x7C87,0x4CE4,0x5CC5,0x2C22,0x3C03,0x0C60,0x1C41,
                0xEDAE,0xFD8F,0xCDEC,0xDDCD,0xAD2A,0xBD0B,0x8D68,0x9D49,
                0x7E97,0x6EB6,0x5ED5,0x4EF4,0x3E13,0x2E32,0x1E51,0x0E70,
                0xFF9F,0xEFBE,0xDFDD,0xCFFC,0xBF1B,0xAF3A,0x9F59,0x8F78,
                0x9188,0x81A9,0xB1CA,0xA1EB,0xD10C,0xC12D,0xF14E,0xE16F,
                0x1080,0x00A1,0x30C2,0x20E3,0x5004,0x4025,0x7046,0x6067,
                0x83B9,0x9398,0xA3FB,0xB3DA,0xC33D,0xD31C,0xE37F,0xF35E,
                0x02B1,0x1290,0x22F3,0x32D2,0x4235,0x5214,0x6277,0x7256,
                0xB5EA,0xA5CB,0x95A8,0x8589,0xF56E,0xE54F,0xD52C,0xC50D,
                0x34E2,0x24C3,0x14A0,0x0481,0x7466,0x6447,0x5424,0x4405,
                0xA7DB,0xB7FA,0x8799,0x97B8,0xE75F,0xF77E,0xC71D,0xD73C,
                0x26D3,0x36F2,0x0691,0x16B0,0x6657,0x7676,0x4615,0x5634,
                0xD94C,0xC96D,0xF90E,0xE92F,0x99C8,0x89E9,0xB98A,0xA9AB,
                0x5844,0x4865,0x7806,0x6827,0x18C0,0x08E1,0x3882,0x28A3,
                0xCB7D,0xDB5C,0xEB3F,0xFB1E,0x8BF9,0x9BD8,0xABBB,0xBB9A,
                0x4A75,0x5A54,0x6A37,0x7A16,0x0AF1,0x1AD0,0x2AB3,0x3A92,
                0xFD2E,0xED0F,0xDD6C,0xCD4D,0xBDAA,0xAD8B,0x9DE8,0x8DC9,
                0x7C26,0x6C07,0x5C64,0x4C45,0x3CA2,0x2C83,0x1CE0,0x0CC1,
                0xEF1F,0xFF3E,0xCF5D,0xDF7C,0xAF9B,0xBFBA,0x8FD9,0x9FF8,
                0x6E17,0x7E36,0x4E55,0x5E74,0x2E93,0x3EB2,0x0ED1,0x1EF0
            ]


OnCANPreFDevent = TSMasterAPI.OnTx_RxFUNC_CANFD(On_CANFD_EVENT_Pre)


new_rolling_data = []
run_faule_count = 0
corrRespStr = ""
aes_bytes = []
randNumber = []
startTime = 0
isRestart = False

'''监控CAN数据'''
def check_PCAN(OBJ, ACAN2):
    global new_rolling_data
    global run_faule_count
    global corrRespStr
    global aes_bytes
    global randNumber
    global startTime
    global isRestart

    current_time = time.time()
    if startTime > 0 and current_time - startTime >= 360 and isRestart:
        logging.info('360秒未收到OBU数据,将断电重启')
        conftest.__Driver['power'].turn_off_power()
        time.sleep(0.5)
        conftest.__Driver['power'].turn_on_power()
        # 重启后重新计算
        startTime = time.time()
            

    if ACAN2.contents.FIdxChn == 0 and ACAN2.contents.FIdentifier == 0x5DC:
        startTime = time.time()
        isRestart = False

    if ACAN2.contents.FIdxChn == 0 and ACAN2.contents.FIdentifier == Set_up.resID:
        if ACAN2.contents.FData[1:3] == [0x50,0x02]:
            # 进入boot后，OBU不在发送信号
            isRestart = False
        if ACAN2.contents.FData[1:3] == [0x50,0x01]:
            Set_up.startCheck = True
            BRS = ACAN2.contents.FFDProperties & 0x02 >> 1
            EDL = ACAN2.contents.FFDProperties & 0x01
            logging.info(f'收到5001,brs={BRS}, EDL={EDL}, 开始比对PCAN数据')
            isRestart = True

            
    if (ACAN2.contents.FIdentifier == Set_up.resID and ACAN2.contents.FIdxChn == 0):
        if ACAN2.contents.FData[1] == 0x7F and ACAN2.contents.FData[3] == 0x78:
           logging.info("收到NRC78")
           #if Set_up.IsRestore:
                #Set_up.ClosePower = True
    
    if ACAN2.contents.FIdentifier == 0x0BB and ACAN2.contents.FIdxChn == 0:
        if Set_up.CalcCMAC and aes_bytes == []:
            randNumber = ACAN2.contents.FData[0:8]
            corrRespStr = binascii.b2a_hex(bytearray(randNumber), ' ')
            logging.info(f'收到OBU发送的随机数: {corrRespStr}')
    
    # if (Set_up.testVin != [] and randNumber != []):
            vinKey = Set_up.testVin[0:8] + Set_up.testVin[-8:]  
            aes_bytes = list(AESCMAC.calculate(randNumber, vinKey))[0:8]
            corrRespStr = binascii.b2a_hex(bytearray(aes_bytes), ' ')
            logging.info(f'计算的OBU防拆认证码: {corrRespStr}')
            conftest.__Driver['CAN'].TransmitData(0xBA, aes_bytes, 'CANFD')
    
    

OnCANPreFDevent_checkPCAN = TSMasterAPI.OnTx_RxFUNC_CANFD(check_PCAN)




'''CRC-8校验计算'''
# Crc8_8H2F 多项式0x12F
Crc8_8H2Ftable = [  0x00, 0x2F, 0x5E, 0x71, 0xBC, 0x93, 0xE2, 0xCD, 0x57, 0x78, 0x09, 0x26, 0xEB, 0xC4, 0xB5, 0x9A,
                    0xAE, 0x81, 0xF0, 0xDF, 0x12, 0x3D, 0x4C, 0x63, 0xF9, 0xD6, 0xA7, 0x88, 0x45, 0x6A, 0x1B, 0x34,
                    0x73, 0x5C, 0x2D, 0x02, 0xCF, 0xE0, 0x91, 0xBE, 0x24, 0x0B, 0x7A, 0x55, 0x98, 0xB7, 0xC6, 0xE9,
                    0xDD, 0xF2, 0x83, 0xAC, 0x61, 0x4E, 0x3F, 0x10, 0x8A, 0xA5, 0xD4, 0xFB, 0x36, 0x19, 0x68, 0x47,
                    0xE6, 0xC9, 0xB8, 0x97, 0x5A, 0x75, 0x04, 0x2B, 0xB1, 0x9E, 0xEF, 0xC0, 0x0D, 0x22, 0x53, 0x7C,
                    0x48, 0x67, 0x16, 0x39, 0xF4, 0xDB, 0xAA, 0x85, 0x1F, 0x30, 0x41, 0x6E, 0xA3, 0x8C, 0xFD, 0xD2,
                    0x95, 0xBA, 0xCB, 0xE4, 0x29, 0x06, 0x77, 0x58, 0xC2, 0xED, 0x9C, 0xB3, 0x7E, 0x51, 0x20, 0x0F,
                    0x3B, 0x14, 0x65, 0x4A, 0x87, 0xA8, 0xD9, 0xF6, 0x6C, 0x43, 0x32, 0x1D, 0xD0, 0xFF, 0x8E, 0xA1,
                    0xE3, 0xCC, 0xBD, 0x92, 0x5F, 0x70, 0x01, 0x2E, 0xB4, 0x9B, 0xEA, 0xC5, 0x08, 0x27, 0x56, 0x79,
                    0x4D, 0x62, 0x13, 0x3C, 0xF1, 0xDE, 0xAF, 0x80, 0x1A, 0x35, 0x44, 0x6B, 0xA6, 0x89, 0xF8, 0xD7,
                    0x90, 0xBF, 0xCE, 0xE1, 0x2C, 0x03, 0x72, 0x5D, 0xC7, 0xE8, 0x99, 0xB6, 0x7B, 0x54, 0x25, 0x0A,
                    0x3E, 0x11, 0x60, 0x4F, 0x82, 0xAD, 0xDC, 0xF3, 0x69, 0x46, 0x37, 0x18, 0xD5, 0xFA, 0x8B, 0xA4,
                    0x05, 0x2A, 0x5B, 0x74, 0xB9, 0x96, 0xE7, 0xC8, 0x52, 0x7D, 0x0C, 0x23, 0xEE, 0xC1, 0xB0, 0x9F,
                    0xAB, 0x84, 0xF5, 0xDA, 0x17, 0x38, 0x49, 0x66, 0xFC, 0xD3, 0xA2, 0x8D, 0x40, 0x6F, 0x1E, 0x31,
                    0x76, 0x59, 0x28, 0x07, 0xCA, 0xE5, 0x94, 0xBB, 0x21, 0x0E, 0x7F, 0x50, 0x9D, 0xB2, 0xC3, 0xEC,
                    0xD8, 0xF7, 0x86, 0xA9, 0x64, 0x4B, 0x3A, 0x15, 0x8F, 0xA0, 0xD1, 0xFE, 0x33, 0x1C, 0x6D, 0x42,
                ]

def Crc8_8H2F(pData,len):
    crc8 = 0xFF
    for i in range(len-1):
        crc8 ^= pData[i]
        crc8 = Crc8_8H2Ftable[crc8]
    #     logging.info("Crc8 = {0}".format(crc8))
    # crc8 ^= pData[0]
    # crc8 = Crc8_8H2Ftable[crc8]
    # logging.info("Crc8 = {0}".format(crc8))
    crc8 ^= 0xFF
    return crc8

# modbus = Modbus('COM8')


class real_CAN_TC(TSCAN):

    '''0x1C2 checksum and rollingcounter'''
    def set_0x1C2_rolling_counter_error(self,enable=False):
        global Enable_0x1C2_RollingCounter_Error
        if enable:
            Enable_0x1C2_RollingCounter_Error = True
        else:
            Enable_0x1C2_RollingCounter_Error = False
    
    def set_0x1C2_checksum_error(self,enable=False):
        global Enable_0x1C2_Checksum_Error
        if enable:
            logging.info("Set 0x1C2 Checksum mistake")
            Enable_0x1C2_Checksum_Error = True
        else:
            logging.info("Set 0x1C2 Checksum correct")
            Enable_0x1C2_Checksum_Error = False 

    def sendFrame(self, Reqstr, iStep, isPrint = True):
        # 判断传入变量类型
        if isinstance(Reqstr, str):
            req = self.strToToHexBytes(Reqstr)
        else:
            req = Reqstr
        with allure.step(f"第{iStep}步:发送CAN帧 # NA"):
            self.TransmitData(Set_up.reqID, req, Set_up.CAN_Type, isPrint)
            iStep += 1
        return iStep

    
    def calcKey(self,seed):
        TOPBIT = 0x8000
        POLYMON_1 = 0x8408   
        POLYNOM_2 = 0x8025
        BITMASK  = 0x0080
        INITIAL_REMINDER = 0xFFFE
        MSG_LEN = 2 #seed length in bytes
        bSeed = [0,0]
        remainder = INITIAL_REMINDER
        bSeed[0] = seed[0]  #MSB
        bSeed[1] = seed[1] #LSB
        key = [0,0]

        for n in range(MSG_LEN):
            remainder ^= ((bSeed[n]&0xFF) << 8)
            for i in range(8):
                if (remainder & TOPBIT):
                    if(remainder & BITMASK):
                        remainder = (remainder << 1) ^ POLYMON_1
                    else:
                        remainder = (remainder << 1) ^ POLYNOM_2
                else:
                    remainder = (remainder << 1)
        key[0] = (remainder&0xFFFF)>>8
        key[1] = remainder&0xFF
        logging.info(key)
        return key

    '''BYD密钥计算'''
    def byd_calcKey(self,seed):
        KeyKValue = 0x0094
        key = [0,0,0,0]

        iSeed = seed[0] << 24|seed[1] << 16|seed[2] << 8 |seed[3]
        KeyResult = ((((iSeed >> 1) ^ iSeed) << 3) ^ (iSeed >> 2))
        STccKey = KeyResult ^ KeyKValue
        key[0] = (STccKey>>24) &0xFF
        key[1] = (STccKey>>16) &0xFF
        key[2] = (STccKey>>8) &0xFF
        key[3] = STccKey &0xFF
        return key

    '''设置信号使能状态'''
    def setSignalEanble(self, message, status, iStep, nodeName = Set_up.nodeName):
        '''
        功能：
            1. 设置信号使能状态
        参数：
            message: 消息
            status: 状态
            waitTime: 等待时间
            iStep: 步骤
        返回：
            iStep: 步骤
        '''
        if status == False:
            with allure.step(f'Step{iStep}: 设置 {message} 信号停发 # NA'):
                self.Enable_rbs_message(Set_up.NetName, nodeName, message, False) 
        else:
            with allure.step(f'Step{iStep}: 设置 {message} 信号发送 # NA'):
                self.Enable_rbs_message(Set_up.NetName, nodeName, message, True) 
        iStep += 1
        return iStep

    '''设置 0x0BA 信号使能状态'''
    def set_0x0BA_Enable(self, status, waitTime, iStep):
        '''
        功能：
            1. 设置 0x0BA 信号使能状态
        参数：
            status: 状态
            waitTime: 等待时间
            iStep: 步骤
        返回：
            iStep: 步骤
        '''
        if status == False:
            with allure.step(f'Step{iStep}: 设置 0x0BA 信号停发 # NA'):
                self.Enable_rbs_message(Set_up.NetName, 'VIU0', '0x0BA', False)
        else:
            with allure.step(f'Step{iStep}: 设置 0x0BA 信号发送 # NA'):
                self.Enable_rbs_message(Set_up.NetName, 'VIU0', '0x0BA', True)
        logging.info(f'wait {waitTime} s')
        time.sleep(waitTime)
        iStep += 1
        return iStep

    '''设置 0x340 信号使能状态'''
    def set_0x340_Enable(self, status, waitTime, iStep):
        '''
        功能：
            1. 设置 0x340 信号使能状态
        参数：
            status: 状态
            waitTime: 等待时间
            iStep: 步骤
        返回：
            iStep: 步骤
        '''
        if status == False:
            with allure.step(f'Step{iStep}: 设置 0x340 信号停发 # NA'):
                self.Enable_rbs_message(Set_up.NetName, 'VIU0', 'VIU_Route_Body3', False) 
        else:
            with allure.step(f'Step{iStep}: 设置 0x340 信号发送 # NA'):
                self.Enable_rbs_message(Set_up.NetName, 'VIU0', 'VIU_Route_Body3', True)
        logging.info(f'wait {waitTime} s') 
        time.sleep(waitTime)
        iStep += 1
        return iStep

    '''设置 0x3AE 信号使能状态'''
    def set_0x3AE_Enable(self, status, waitTime, iStep):
        '''
        功能：
            1. 设置 0x3AE 信号使能状态
        参数：
            status: 状态
            waitTime: 等待时间
            iStep: 步骤
        返回：
            iStep: 步骤
        '''
        if status == False:
            with allure.step(f'Step{iStep}: 设置 0x3AE 信号停发 # NA'):
                self.Enable_rbs_message(Set_up.NetName, 'VIU0', '0x3AE', False)
        else:
            with allure.step(f'Step{iStep}: 设置 0x3AE 信号发送 # NA'):
                self.Enable_rbs_message(Set_up.NetName, 'VIU0', '0x3AE', True)
        logging.info(f'wait {waitTime} s')
        time.sleep(waitTime)
        iStep += 1
        return iStep
    
    '''设置 0x5E2 信号使能状态'''
    def set_0x5E2_Enable(self, status, waitTime, iStep):
        '''
        功能：
            1. 设置 0x5E2 信号使能状态
        参数：
            status: 状态
            waitTime: 等待时间
            iStep: 步骤
        返回：
            iStep: 步骤
        '''
        if status == False:
            with allure.step(f'Step{iStep}: 设置 0x5E2 信号停发 # NA'):
                self.Enable_rbs_message(Set_up.NetName, 'VIU0', '0x5E2', False)     
        else:
            with allure.step(f'Step{iStep}: 设置 0x5E2 信号发送 # NA'):
                self.Enable_rbs_message(Set_up.NetName, 'VIU0', '0x5E2', True)
        logging.info(f'wait {waitTime} s')
        time.sleep(waitTime)
        iStep += 1
        return iStep

    '''设置信号值'''
    def setSignalValue(self, message, signal, value, iStep):
        '''
        功能：
            1. 设置信号值
        参数：
            message: 消息
            signal: 信号
            value: 值
            iStep: 步骤
        返回：
            iStep: 步骤
        '''
        with allure.step(f'Step{iStep}: 设置{message}信号的{signal}值为{value} # NA'):
            self.write_db_signal(Set_up.NetName,Set_up.nodeName, message, signal, value)
            time.sleep(0.5)
            iStep += 1
            self.write_db_signal(Set_up.NetName,Set_up.nodeName, message, signal, value)
            time.sleep(0.5)
            iStep += 1
            readValue = self.read_db_signal(Set_up.NetName,Set_up.nodeName, message, signal)
        with allure.step(f'Step{iStep}: 读取{message}信号的{signal}值 # 应是{value}'):
            iStep += 1
        return iStep

    '''转换字符串为数组'''
    def strToToHexBytes(self,hexString):
        hexString = str(hexString).replace(' ', '').replace('[', '').replace(']', '').replace('\'', '')

        if ((len(hexString) % 2) != 0):
            hexString += " "

        c_16_temp = list(hexString)
        ilen = int(len(c_16_temp) / 2)
        hexBytes = [0] * ilen
        for index in range(ilen):
            hexBytes[index] = int((c_16_temp[2 * index] + c_16_temp[2 * index + 1]),16)
        return hexBytes
   
    def unlock(self, Driver, iStep, level = 1):
        try:
            seed = []
            if level == 1:
                with allure.step(f"Step{iStep}:Request:0x27 0x01 # Response:0x67,0x01 xx xx xx xx"):
                    uds_rep,size = Driver['UDS'].req_and_res_can([0x27,0x01], 2)
                    with assume: assert (uds_rep[0:2] == [0x67,0x01] and size == 18)
                    for i in range(16):
                        if size == 18:
                            seed.append(uds_rep[2+i])
                        else:
                            seed.append(0)
                    key = list(AESCMAC.calculate(seed, [0xF4, 0x6B, 0x20, 0xEA, 0xFE, 0x88, 0x98, 0x58, 0x27, 0xBB, 0x7C, 0x9A, 0xC7, 0x32, 0xE4, 0x66]))  # Convert bytes to list
                    send_key = [0x27,0x02] + key

                iStep = iStep + 1
                with allure.step(f"Step{iStep}:Request:{0} # Response:".format(binascii.b2a_hex(bytearray(send_key), ' '))):
                    uds_rep,size = Driver['UDS'].req_and_res_can(send_key, 18)
                    with assume: assert uds_rep[0:2] == [0x67,0x02]
            else:
                with allure.step(f"Step{iStep}:Request:0x27 0x11 # Response:0x67,0x11 xx xx xx xx"):
                    uds_rep,size = Driver['UDS'].req_and_res_can([0x27,0x11],2)
                    with assume: assert (uds_rep[0:2] == [0x67,0x11] and size == 18)
                    for i in range(16):
                        if size == 18:
                            seed.append(uds_rep[2+i])
                        else:
                            seed.append(0)
                
                    key = list(AESCMAC.calculate(seed, [0xB7,0xD7,0xD3,0x1C,0x54,0xA1,0xC6,0x25,0x71,0x7A,0x70,0x84,0xE6,0x39,0x86,0x88])) # Convert bytes to list
                    send_key = [0x27,0x12] + key

                iStep = iStep + 1
                with allure.step(f"Step{iStep}:Request:{0} # Response:".format(binascii.b2a_hex(bytearray(send_key), ' '))):
                    uds_rep,size = Driver['UDS'].req_and_res_can(send_key,18)
                    with assume: assert uds_rep[0:2] == [0x67,0x12]
            
            return iStep + 1
        except Exception as e:
            logging.info(f"{e}".center(50, '-'))

    ''' Reqstr=发送信息 Respstr=接收信息 session=会话模式 iStep=步骤几 unlock=会话模式切换完成是否需要安全认证'''
    def checkReqResp(self, Driver, Reqstr, Respstr, session, iStep, unlock=False, addressing='UDS', isClearFault='noClearFault'):

        # 判断传入变量类型
        if isinstance(Reqstr, str):
            req = self.strToToHexBytes(Reqstr)
        else:
            req = Reqstr
        if isinstance(Respstr, str):
            resp = self.strToToHexBytes(Respstr)
        else:
            resp = Respstr

        # 会话模式
        if session==1 or session==3 or session == 2:
            with allure.step(f'Step{iStep}: Request 0x10 0x01 # Response: 0x50,0x01'):
                uds_resp, size = Driver['UDS'].req_and_res_can([0x10, 0x01], 2)
                with assume:assert uds_resp[0:2] == [0x50, 0x01]
                iStep = iStep + 1
                time.sleep(0.5)
            # with allure.step(f'Step{iStep}: Request 0x22 0xF1 0x86 # Response: 0x62,0xF1,0x86 0x01'):
            #     uds_resp, size = Driver['UDS'].req_and_res_can([0x22, 0xF1, 0x86], 3)
            #     with assume: assert uds_resp[0:4] == [0x62,0xF1,0x86, 0x01]
            #     iStep = iStep + 1
        if session == 3 or session == 2:
            with allure.step(f'Step{iStep}: Request 0x10 0x03 # Response: 0x50,0x03'):
                uds_resp, size = Driver['UDS'].req_and_res_can([0x10, 0x03], 2)
                with assume:assert uds_resp[0:2] == [0x50, 0x03]
                iStep = iStep + 1
                time.sleep(0.5)
            # with allure.step(f'Step{iStep}: Request 0x22 0xF1 0x86 # Response: 0x62,0xF1,0x86 0x03'):
            #     uds_resp, size = Driver['UDS'].req_and_res_can([0x22, 0xF1, 0x86], 3)
            #     with assume: assert uds_resp[0:4] == [0x62,0xF1,0x86, 0x03]
            #     iStep = iStep + 1
        if session==2:
            # iStep = self.unlock(Driver,iStep)
            with allure.step(f'Step{iStep}: Request 0x10 0x02 # Response: 0x50,0x02'):
                uds_resp, size = Driver['UDS'].req_and_res_can([0x10, 0x02], 2)
                with assume:assert uds_resp[0:2] == [0x50, 0x02]
                iStep = iStep + 1
                time.sleep(1)
            # with allure.step(f'Step{iStep}: Request 0x22 0xF1 0x86 # Response: 0x62,0xF1,0x86 0x02'):
            #     uds_resp, size = Driver['UDS'].req_and_res_can([0x22, 0xF1, 0x86], 3)
            #     with assume: assert uds_resp[0:4] == [0x62,0xF1,0x86, 0x02]
            #     iStep = iStep + 1
        if unlock:
            if session == 2:
                iStep = self.unlock(Driver,iStep,2)
            else:
                iStep = self.unlock(Driver,iStep, 1)

        '''清除故障''' 
        if isClearFault != 'noClearFault':
            with allure.step(f"Step{iStep}:Request 0x14 0xFF 0xFF 0xFF # Response: 0x54"):
                uds_rep, size = Driver['UDS'].req_and_res_can([0x14, 0xFF, 0xFF, 0xFF], 4)
                iStep +=1 
                reqstr = binascii.b2a_hex(bytearray(uds_rep), ' ')
                with assume: assert uds_rep[0] == 0x54
                iStep +=1 

        reqstr = binascii.b2a_hex(bytearray(req), ' ')
        respstr = binascii.b2a_hex(bytearray(resp), ' ')
        
        with allure.step(f'Step{iStep}: Request {reqstr} # Response: {respstr}'): 
            uds_resp, size = Driver[addressing].req_and_res_can(req, len(req))
            
            # if len(uds_resp) == 0 and len(resp) != 0:
            #     # 概率出现无响应问题,重新操作一遍
            #     if addressing == 'UDS':
            #         uds_resp, size = Driver['UDS'].req_and_res_can(req, len(req)) 
            #     else:
            #         uds_resp, size = Driver['UDS'].req_and_res_can(req, len(req), True)


            if len(resp) == 0 :
                actuRespStr = binascii.b2a_hex(bytearray(uds_resp[0:len(uds_resp)]), ' ') 
                with assume: assert 0 == size,logging.error(f'correct response:[无响应], actual response:[{actuRespStr}]')
            elif len(uds_resp) == 0:
                with assume: assert len(resp) == size
                corrRespStr = binascii.b2a_hex(bytearray(resp), ' ')
                actuRespStr = binascii.b2a_hex(bytearray(uds_resp[0:len(uds_resp)]), ' ')   
                logging.error(f'correct response:[{corrRespStr}], actual response:[{actuRespStr}]') 
            else:    
                if uds_resp[0:len(resp)] != resp:
                    corrRespStr = binascii.b2a_hex(bytearray(resp), ' ')
                    actuRespStr = binascii.b2a_hex(bytearray(uds_resp[0:len(uds_resp)]), ' ')   
                    logging.error(f'correct response:[{corrRespStr}], actual response:[{actuRespStr}]')  
                with assume: 
                    assert uds_resp[0:len(resp)] == resp
                    if req == [0x27,0x01] and resp[0] != 0x7F:
                        assert size == 18
        return iStep + 1,uds_resp, size


    def checkReqRespNoWait(self, Driver, Reqstr, Respstr, session, iStep=1, unlock=False, addressing='UDS', isClearFault='noClearFault'):
        """
        检测发送与接收是否符合预期函数,会话切换无等待时间.
        Parameters:
            Driver - Driver. 
            Reqstr - 请求信息
            Respstr - 期待返回信息  
            session - 会话模式  
            iStep - 第几步,默认为步骤1. 
            unlock - 是否安全解锁,默认不安全解锁 
            addressing - 寻址方式,默认物理寻址 
            isClearFault - 是否清除故障,默认不清除    

        Returns:
            iStep - 第几步
            uds_resp - 返回信息 
            size - 信息长度
        """

        # 判断传入变量类型
        if isinstance(Reqstr, str):
            req = self.strToToHexBytes(Reqstr)
        else:
            req = Reqstr
        if isinstance(Respstr, str):
            resp = self.strToToHexBytes(Respstr)
        else:
            resp = Respstr

        # 会话模式
        if session==1 or session==3 or session == 2:
            with allure.step(f'Step{iStep}: Request 0x10 0x01 # Response: 0x50,0x01'):
                uds_resp, size = Driver['UDS'].req_and_res_can([0x10, 0x01], 2)
                with assume:assert uds_resp[0:2] == [0x50, 0x01]
                iStep = iStep + 1
            # with allure.step(f'Step{iStep}: Request 0x22 0xF1 0x86 # Response: 0x62,0xF1,0x86 0x01'):
            #     uds_resp, size = Driver['UDS'].req_and_res_can([0x22, 0xF1, 0x86], 3)
            #     with assume: assert uds_resp[0:4] == [0x62,0xF1,0x86, 0x01]
            #     iStep = iStep + 1
        if session == 3 or session == 2:
            with allure.step(f'Step{iStep}: Request 0x10 0x03 # Response: 0x50,0x03'):
                uds_resp, size = Driver['UDS'].req_and_res_can([0x10, 0x03], 2)
                with assume:assert uds_resp[0:2] == [0x50, 0x03]
                time.sleep(0.5)
                iStep = iStep + 1
            # with allure.step(f'Step{iStep}: Request 0x22 0xF1 0x86 # Response: 0x62,0xF1,0x86 0x03'):
            #     uds_resp, size = Driver['UDS'].req_and_res_can([0x22, 0xF1, 0x86], 3)
            #     with assume: assert uds_resp[0:4] == [0x62,0xF1,0x86, 0x03]
            #     iStep = iStep + 1
        if session==2:
            # iStep = self.unlock(Driver,iStep)
            with allure.step(f'Step{iStep}: Request 0x10 0x02 # Response: 0x50,0x02'):
                uds_resp, size = Driver['UDS'].req_and_res_can([0x10, 0x02], 2)
                with assume:assert uds_resp[0:2] == [0x50, 0x02]
                time.sleep(1)
                iStep = iStep + 1
            # with allure.step(f'Step{iStep}: Request 0x22 0xF1 0x86 # Response: 0x62,0xF1,0x86 0x02'):
            #     uds_resp, size = Driver['UDS'].req_and_res_can([0x22, 0xF1, 0x86], 3)
            #     with assume: assert uds_resp[0:4] == [0x62,0xF1,0x86, 0x02]
            #     iStep = iStep + 1
        if unlock:
            if session == 2:
                iStep = self.unlock(Driver,iStep,2)
            else:
                iStep = self.unlock(Driver,iStep, 1)

        '''清除故障''' 
        if isClearFault != 'noClearFault':
            with allure.step(f"Step{iStep}:Request 0x14 0xFF 0xFF 0xFF # Response: 0x54"):
                uds_rep, size = Driver['UDS'].req_and_res_can([0x14, 0xFF, 0xFF, 0xFF], 4)
                iStep +=1 
                reqstr = binascii.b2a_hex(bytearray(uds_rep), ' ')
                with assume: assert uds_rep[0] == 0x54
                iStep +=1 
                time.sleep(2)
                
        reqstr = binascii.b2a_hex(bytearray(req), ' ')
        respstr = binascii.b2a_hex(bytearray(resp), ' ')
        
        with allure.step(f'Step{iStep}: Request {reqstr} # Response: {respstr}'):
            uds_resp, size = Driver[addressing].req_and_res_can(req, len(req))

            if len(resp) == 0 :
                with assume: assert 0 == size,logging.error(f'correct response:[无响应], actual response:[{uds_resp}]')
            elif len(uds_resp) == 0:
                with assume: assert len(resp) == size
                corrRespStr = binascii.b2a_hex(bytearray(resp), ' ')
                actuRespStr = binascii.b2a_hex(bytearray(uds_resp[0:len(uds_resp)]), ' ')   
                logging.error(f'correct response:[{corrRespStr}], actual response:[{actuRespStr}]') 
            else:    
                if uds_resp[0:len(resp)] != resp:
                    corrRespStr = binascii.b2a_hex(bytearray(resp), ' ')
                    actuRespStr = binascii.b2a_hex(bytearray(uds_resp[0:len(uds_resp)]), ' ')   
                    logging.error(f'correct response:[{corrRespStr}], actual response:[{actuRespStr}]')  
                with assume: 
                    assert uds_resp[0:len(resp)] == resp
                    if req == [0x27,0x01] and resp[0] != 0x7F:
                        assert size == 18

        return iStep + 1,uds_resp, size
    
    def Send_And_Receive(self, Driver, Reqstr, iStep, timeout=2, isRecv=True, reqID = Set_up.reqID, respID = Set_up.resID):
        '''
        发送并接收CAN消息
        Args:
            Driver: 包含CAN驱动的测试驱动对象
            Reqstr: 发送的请求字符串
            iStep: 测试步骤
            timeout: 超时时间
            isRecv: 是否接收后续连续帧响应
        Returns:
            iStep: 测试步骤
            uds_resp: 接收的响应数据
            size: 响应数据长度
        '''
        # 判断传入变量类型
        if isinstance(Reqstr, str):
            req = self.strToToHexBytes(Reqstr)
        else:
            req = Reqstr
      
        if len(req) < 8:
            req += [0] * (8 - len(req)) 
        reqstr = binascii.b2a_hex(bytearray(req), ' ')

        Driver['CAN'].ClearBuffer()  # 清缓存

        with allure.step(f'Step{iStep}: 请求{reqstr} # 响应 '):
            import time
            ret = Driver['CAN'].TransmitData(reqID, req, Set_up.CAN_Type, True)
            respData = []
            start_time = time.time()
            response_time = None
            timeout_time = start_time + timeout
            ALl_data = None
            buffer_size = 0
            
            # 初始化总帧数和已接收帧数
            total_frames = None
            received_frames = 0
            respData = []

            # 循环等待首帧响应，直到超时
            while time.time() < timeout_time:
                ALl_data, buffer_size = Driver['CAN'].read_frame()
                if buffer_size > 0:
                    response_data = ALl_data[ALl_data['AIdentifier'] == respID]
                    if len(response_data) > 0:
                        # 计算首帧响应时间
                        end_time = time.time()
                        response_time = (end_time - start_time) * 1000  # 转换为毫秒

                        # 从首帧数据中计算总帧数（假设首帧数据可以计算出帧数）
                        first_frame_data = response_data.iloc[0]['ADatas']
                        # 根据UDS协议(ISO 14229-2)计算总帧数：首帧PCI字段解析数据长度
                        # PCI格式：高4位为0x1(首帧标识)，低4位+第二个字节组成总数据长度
                        if len(first_frame_data) >= 2:
                            # 提取PCI字段中的数据长度信息
                            data_length = ((first_frame_data[0] & 0x0F) << 8) | first_frame_data[1]
                            # 计算总帧数：首帧已包含6字节数据，后续每帧最多7字节
                            # 使用整数除法并向上取整确保完整接收所有数据
                            if data_length <= 6:
                                total_frames = 1
                            else:
                                total_frames = (data_length - 6 + 6) // 7 + 1  # +6实现向上取整效果
                            total_frames = int(total_frames)
                        else:
                            total_frames = 0
                            logging.warning("首帧数据长度不足，无法计算总帧数")

                        # 累加首帧数据
                        respData += first_frame_data[0:8]
                        received_frames += 1

                        break
                time.sleep(0.001)  # 短暂延迟避免CPU占用过高

            # 记录响应时间日志
            if response_time is not None:
                logging.info(f"首帧响应时间: {response_time:.2f}ms")
                allure.attach(f"首帧响应时间: {response_time:.2f}ms", name="响应时间", attachment_type=allure.attachment_type.TEXT)
            else:
                logging.warning(f"未在{timeout}秒内收到首帧响应")

            # 若不接收后续连续帧响应，直接返回
            if not isRecv:
                return iStep + 1, respData, None

            # 继续接收剩余数据，直到收到所有数据或超时
            while total_frames is not None and received_frames < total_frames and time.time() < timeout_time:
                ALl_data, buffer_size = Driver['CAN'].read_frame()
                if buffer_size > 0:
                    response_data = ALl_data[ALl_data['AIdentifier'] == Set_up.resID]
                    for i in range(len(response_data)):
                        frame_data = response_data.iloc[i]['ADatas']
                        respData += frame_data[0:8]
                        received_frames += 1
                        if received_frames >= total_frames:
                            break
                time.sleep(0.001)  # 短暂延迟避免CPU占用过高

            # 处理最终接收到的数据
            actuRespStr = binascii.b2a_hex(bytearray(respData), ' ') 
            logging.info('responseID:[0x{0:X}], Data:[{1}]'.format(Set_up.resID, actuRespStr))

            return iStep + 1, respData, response_time

    '''设置电源电压'''
    def SetVolt(self, Driver, VoltNumber, waitTime, iStep):
        with allure.step(f"Step{iStep}:Set voltage = {VoltNumber}V"):
            Driver['power'].set_voltage(VoltNumber)
            # logging.info(f'Set volt to {VoltNumber}V')
        iStep += 1
        time.sleep(waitTime)
        logging.info(f'wait {waitTime} s')
        return iStep


    def V_ConnectCanH_GND(self, Eanble,iStep):
        modbus = Modbus(Set_up.modbusCOM)
        if Eanble:
            self.Reset_Setup_Pcanlist(True)
            with allure.step(f"Step{iStep}:VCANH connects to VCANL"):
                modbus.write_single_coil(DO_Address_single.Y0.value,0x0001)
                time.sleep(6)
        else:
            with allure.step(f"Step{iStep}:Remove fault of VCANH connnects to VCANL"):
                modbus.write_single_coil(DO_Address_single.Y0.value,0x0000)
            time.sleep(1)
        iStep += 1
        del modbus

        return iStep

    def V_ConnectCanL_Vcc(self, Eanble,iStep):
        
        modbus = Modbus(Set_up.modbusCOM)
        if Eanble:
            self.Reset_Setup_Pcanlist(True)
            with allure.step(f"Step{iStep}:VCANH connects to VCANL"):
                modbus.write_single_coil(DO_Address_single.Y1.value,0x0001)
                time.sleep(6)
        else:
            with allure.step(f"Step{iStep}:Remove fault of VCANH connnects to VCANL"):
                modbus.write_single_coil(DO_Address_single.Y1.value,0x0000)
            time.sleep(1)
        iStep += 1
        del modbus
        return iStep

    def V_ConnectCanH_CanL(self, Driver, Eanble,iStep):
        if Eanble:
            self.Reset_Setup_Pcanlist(True)
            with allure.step(f"Step{iStep}: 短接CANH与CANL # NA"):
                iStep = self.SetJiDianQiStatus(Driver, iStep, 6, 1)
                time.sleep(6)
        else:
            with allure.step(f"Step{iStep}:恢复CANH与CANL # NA"):
                iStep = self.SetJiDianQiStatus(Driver, iStep, 6, 0)
            time.sleep(1)
        iStep += 1
        return iStep

    def S_ConnectCanH_GND(self, Eanble,iStep):
        modbus = Modbus(Set_up.modbusCOM)
        if Eanble:
            self.Reset_Setup_Pcanlist(True)
            with allure.step(f"Step{iStep}:SCANH connects to GND"):
                modbus.write_single_coil(DO_Address_single.Y4.value,0x0001)
                time.sleep(6)
        else:
            with allure.step(f"Step{iStep}:Remove fault of SCANH connnects to GND"):
                modbus.write_single_coil(DO_Address_single.Y4.value,0x0000)
            time.sleep(1)
        iStep += 1
        del modbus
        return iStep

    def S_ConnectCanL_Vcc(self, Eanble,iStep):
        modbus = Modbus(Set_up.modbusCOM)
        if Eanble:
            self.Reset_Setup_Pcanlist(True)
            with allure.step(f"Step{iStep}:SCANH connects to VCC"):
                modbus.write_single_coil(DO_Address_single.Y5.value,0x0001)
                time.sleep(6)
        else:
            with allure.step(f"Step{iStep}:Remove fault of SCANH connnects to VCC"):
                modbus.write_single_coil(DO_Address_single.Y5.value,0x0000)
            time.sleep(1)
        iStep += 1
        del modbus
        return iStep

    def S_ConnectCanH_CanL(self, Eanble,iStep):
        modbus = Modbus(Set_up.modbusCOM)
        if Eanble:
            self.Reset_Setup_Pcanlist(True)
            with allure.step(f"Step{iStep}:SCANH connects to SCANL"):
                modbus.write_single_coil(DO_Address_single.Y6.value,0x0001)
                time.sleep(6)
                logging.info('Unicom SFCAN CanH_CanL,Wait 6s')
        else:
            with allure.step(f"Step{iStep}:Remove fault of SCANH connnects to SCANL"):
                modbus.write_single_coil(DO_Address_single.Y6.value,0x0000)
                logging.info('Disconnect SFCAN CanH_CanL')
            time.sleep(1.5)
        iStep += 1
        del modbus
        return iStep

    def set_signal_Year(self,value):
        '''设置信号年'''
        self.write_db_signal(Set_up.NetName, 'VIU0','TBOX_TimeInfo_VV03','TBOX_TimeYear',value) 
        self.read_db_signal(Set_up.NetName, 'VIU0','TBOX_TimeInfo_VV03','TBOX_TimeYear')  

    def set_signal_Month(self,value):
        '''设置信号月'''
        self.write_db_signal(Set_up.NetName, 'VIU0','TBOX_TimeInfo_VV03','TBOX_TimeMonth',value)
        self.read_db_signal(Set_up.NetName, 'VIU0','TBOX_TimeInfo_VV03','TBOX_TimeMonth')

    def set_signal_Day(self,value):
        '''设置信号日'''
        self.write_db_signal(Set_up.NetName, 'VIU0','TBOX_TimeInfo_VV03','TBOX_TimeDay',value)
        self.read_db_signal(Set_up.NetName, 'VIU0','TBOX_TimeInfo_VV03','TBOX_TimeDay')

    def set_signal_Hour(self,value):
        '''设置信号时'''
        self.write_db_signal(Set_up.NetName, 'VIU0','TBOX_TimeInfo_VV03','TBOX_TimeHour',value)
        self.read_db_signal(Set_up.NetName, 'VIU0','TBOX_TimeInfo_VV03','TBOX_TimeHour')

    def set_signal_Minute(self,value):
        '''设置信号分'''
        self.write_db_signal(Set_up.NetName, 'VIU0','TBOX_TimeInfo_VV03','TBOX_TimeMinute',value)
        self.read_db_signal(Set_up.NetName, 'VIU0','TBOX_TimeInfo_VV03','TBOX_TimeMinute')
    
    def set_signal_Second(self,value):
        '''设置信号秒'''
        self.write_db_signal(Set_up.NetName, 'VIU0','TBOX_TimeInfo_VV03','TBOX_TimeSecond',value)
        self.read_db_signal(Set_up.NetName, 'VIU0','TBOX_TimeInfo_VV03','TBOX_TimeSecond')

    def set_signal_Total_Distance(self,value):
        self.write_db_signal(Set_up.NetName,'VIU0','VIU_Route_Body','VDC_TotalRang',value)
        self.read_db_signal(Set_up.NetName,'VIU0','VIU_Route_Body','VDC_TotalRang')

    '''故障注入函数'''
    def setVariantCodeDet(self, Driver, dtcName, PASS=0,iStep=1):
        with allure.step(f'Step{iStep}:故障注入与消除'):
            logging.info("打开外部注入配置信息")
            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'profile 0',10)
            # 打开外部注入配置信息

            
            Driver['CAN2'].sendAndread_CMD_Shell(0x114,f'cmd set gExInj_{dtcName}_Enable 0 C 1 1',10)
            # 设置故障状态 0=PASS -1=FAIL
            if dtcName == 'MasterRdTemp':
                if PASS == 0:
                    PASS = 60.0
                else:
                    PASS = 117.6
                Driver['CAN2'].sendAndread_CMD_Shell(0x114,f'cmd set gExInj_{dtcName}_val 0 f 1 {PASS}',10)
                time.sleep(10)
            else:
                Driver['CAN2'].sendAndread_CMD_Shell(0x114,f'cmd set gExInj_{dtcName}_val 0 s 1 {PASS}',10)

            logging.info(f'{dtcName} 状态为: [{PASS}]') 
            time.sleep(0.5) 
            iStep += 1
            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'cfg_mode 0',10)

        return iStep


    '''看门狗测试'''
    def test_wdg(self, Driver,iStep):
        with allure.step(f'Step{iStep}:看门狗测试'):
            logging.info("打开外部注入配置信息")
            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'profile 0',10)
            # 打开外部注入配置信息
            Driver['CAN2'].sendAndread_CMD_Shell(0x114,f'cmd fsmErrInjection test_wdg',10)
            time.sleep(0.5) 
            iStep += 1
            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'cfg_mode 0',10)

        return iStep

    
    '''获取雷达安装参数'''
    def GetRadarInfo(self, Driver, infoName,iStep=1):
        with allure.step(f'Step{iStep}:获取雷达安装参数'):
            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'profile 0',10)
            # 打开外部注入配置信息
            RadarInfo = float(Driver['CAN2'].sendAndread_CMD_Shell(0x114,f'cmd get {infoName} 0 f 1',10))

            logging.info(f'{infoName} = [{RadarInfo}]') 
            time.sleep(0.5) 
            iStep += 1
            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'cfg_mode 0',10)

        return iStep, RadarInfo

   
    '''断电重启ECU'''
    def restartETC_Power(self,Driver, iStep=1):
        """
        断电重启产品.
        Parameters:
        Driver - Driver.    
        iStep - 第几步,默认为步骤1.       

        Returns:
            第几步
        """
        with allure.step(f'Step{iStep}:ECU下电再次上电'):
            # Driver['CAN'].pause_signal_loop(0x90)
            # time.sleep(0.5)
            Driver['power'].turn_off_power()
            logging.info('Power off the ECU')
            time.sleep(0.5)
            logging.info('Wait 0.5s')
            Driver['power'].turn_on_power()
            # Driver['CAN'].resume_signal_loop(0x90)
            time.sleep(3)
            logging.info('Wait 3s, Restart the device OK')
            iStep += 1
        return iStep
    
    '''请求1101重启ECU'''
    def restartETC_11(self,Driver, iStep=1, waitTime=5):
        """
        使用1101重启产品.
        Parameters:
        Driver - Driver.    
        iStep - 第几步,默认为步骤1.       

        Returns:
            第几步
        """
        with allure.step(f'Step{iStep}:request 1101 restart ECU'):
            iStep, uds_resp, size = self.checkReqResp(Driver, "11 01", "51 01", 0, iStep, False)
            logging.info(f"wait {waitTime} s")
            time.sleep(waitTime)
            iStep += 1
        return iStep

    '''读取Pcan数据'''
    def read_PCAN_Frame(self):
        respData = []
        respLen = 0
        uds_resp, uds_resp_size = self.read_frame()
        for i in range(uds_resp_size):
            if uds_resp['AIdentifier'][i] == Set_up.resID:
                if(uds_resp['ADatas'][0][0] & 0xF0)<< 8 == 1: 
                    respLen =  (uds_resp['ADatas'][0][0] & 0x0F) + uds_resp['ADatas'][0][1] + 2
                else:
                    respLen =  uds_resp['ADatas'][0][0] + 1
                respData =  uds_resp['ADatas'][i][0:8]
                break
        for i in range(1,uds_resp_size):
            respData += uds_resp['ADatas'][i][1:8]
        if uds_resp_size > 0:
            respData = respData[0:(respLen)]
            actuRespStr = binascii.b2a_hex(bytearray(respData), ' ')    
            logging.error('P_CAN responseID:[0x{0:X}], Data:[{1}]'.format(Set_up.resID, actuRespStr)) 

        return respData, respLen

    '''判断除了三个初始故障是否有新的故障产生'''
    def delInitialFailure(self, iStep,data):
        current_DTC = []
        for real_reponse in np.array(data[3:]).reshape(-1,4):
                    current_DTC.append(real_reponse[0:4].tolist())

        with allure.step(f'Step{iStep}: Determine whether there is any other DTC'):
            for m in Set_up.InitialFailure:
                if m in current_DTC:
                    current_DTC.remove(m)
            with assume: assert len(current_DTC) == 0


            actuRespStr = []
            if len(current_DTC) != 0:
                for m in current_DTC:
                    actuRespStr.append(binascii.b2a_hex(bytearray(m), ' '))

                logging.error('Should not exist DTC iS:[{0}]'.format(actuRespStr)) 
            iStep += 1
        
        return iStep

    '''采用故障注入方式清除初始故障'''
    def clearInitialFilure(self,Driver,iStep):
        with allure.step(f'Step{iStep}: Clear DTC [ACD178 ACD278 ACE655]'):
            logging.info("采用故障注入,清除故障[ACD178 ACD278 ACE655]".center(30, '*'))
            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'profile 0',20)

            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'cmd set gExInj_AlignmentNeverDone_Enable 0 C 1 1',20)
            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'cmd set gExInj_AlignmentNeverDone_val 0 s 1 0',20)

            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'cmd set gExInj_AlignmentNotDone_Enable 0 C 1 1',20)
            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'cmd set gExInj_AlignmentNotDone_val 0 s 1 0',20)

            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'cmd set gExInj_VariantCodeDet_Enable 0 C 1 1',20)
            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'cmd set gExInj_VariantCodeDet_val 0 s 1 0',20)
            time.sleep(1)
            iStep += 1
        return iStep

    def Reset_Setup_Pcanlist(self, Status=True):
        Set_up.startCheck = False
        for i in Set_up.PCAN_list:
            Set_up.PCAN_list[i][1] = True
        for i in Set_up.txList: 
            Set_up.txList[i][1] = True 

    def wakeup_ctrl(self,enable,timecycle=1):
        if enable:
            self.wake_ref = self.send_CANFrame_Cycle(0x188,[0x88,0x88,0x00,0x00,0x00,0x00,0x00,0x88],timecycle)#, Set_up.CAN_Type)
        else:
            self.stop_FrANFrame_Cycle(self.wake_ref)
        pass
    
    def send_sync_frame(self):
        timeNumber = time.time()
        syncTime = int(timeNumber)
        logging.info('send s = [{0}]'.format(syncTime))
        send_SYNC_Frame = [0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09]
        packed_bytes = struct.pack('<l', syncTime)
        send_SYNC_Frame[4] = packed_bytes[3]
        send_SYNC_Frame[5] = packed_bytes[2]
        send_SYNC_Frame[6] = packed_bytes[1]
        send_SYNC_Frame[7] = packed_bytes[0]
        send_SYNC_Frame[2] = 0 | (send_SYNC_Frame[2] & 0xF0)
        crcData = list(send_SYNC_Frame[0:9])
        crcData.pop(1)
        crc16 = Crc8_8H2F(crcData,8)
        send_SYNC_Frame[1] = crc16
        send_SYNC_Frame.pop()
        self.TransmitData(0x2F6,send_SYNC_Frame)

        return timeNumber


    def send_fup_frame(self, timeNumber):
        fupTime = int((timeNumber % 1) * 1000000000)
        logging.info('send ns = [{0}]'.format(fupTime))
        send_FUP_Frame = [0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09]
        packed_bytes = struct.pack('<l', fupTime)
        send_FUP_Frame[4] = packed_bytes[3]
        send_FUP_Frame[5] = packed_bytes[2]
        send_FUP_Frame[6] = packed_bytes[1]
        send_FUP_Frame[7] = packed_bytes[0]
        send_FUP_Frame[2] = 0 | (send_FUP_Frame[2] & 0xF0)
        crcData = list(send_FUP_Frame[0:9])
        crcData.pop(1)
        crc16 = Crc8_8H2F(crcData,8)
        send_FUP_Frame[1] = crc16

        send_FUP_Frame.pop()
        self.TransmitData(0x2F6,send_FUP_Frame)


    '''读取Pcan数据'''
    def read_PCAN_Time_Frame(self, enable = False):
        respData = []
        self.ClearBuffer()
        i = 0
        time.sleep(0.1)
        uds_resp, uds_resp_size = self.read_frame()
        for i in range(uds_resp_size):
            if uds_resp['AIdentifier'][i] == 0x66F:
                respData =  uds_resp['ADatas'][i][0:16]
                readTime = uds_resp['ATimeUs'][i]
                if enable == False:
                    i = 20
                    break
                else:
                    if respData[4] == 0x0C:
                        i = 20
                        break

        for i in range(0,uds_resp_size):
            respData += uds_resp['ADatas'][i][0:16]
        if uds_resp_size > 0:
            respData = respData[0:16]
            actuRespStr = binascii.b2a_hex(bytearray(respData), ' ')    
            logging.error('P_CAN responseID:[0x{0:X}], Data:[{1}]'.format(0x66F, actuRespStr)) 

        return respData, time.time() * 1000
        
    
    def SendAndRead_Eol(self, Driver, send_ID, send_Data, receive_ID, timesleep = 0.08, CanType='CAN'):
        sendstr = binascii.b2a_hex(bytearray(send_Data),' ')
        logging.info('send_ID =    [{0:X}], send_Data =    [{1}]'.format(send_ID, sendstr))
        Driver['CAN2'].ClearBuffer()  # 清缓存
        Driver['CAN2'].TransmitData(send_ID,send_Data,CanType)
        time.sleep(timesleep)
        ALl_data,buffer_size = Driver['CAN2'].read_frame()
        response_data = ALl_data[ALl_data['AIdentifier']== receive_ID]
        receive_data = []
        try:
            len = response_data.iloc[0]['ADLC']
            if len < 9:
                lenth = 8 
            if len == 9:
                lenth = 12
            if len == 10:
                lenth = 16
            if len == 11:
                lenth = 20
            if len == 12:
                lenth = 24
            if len == 13:
                lenth = 32
            if len == 14:
                lenth = 48
            if len == 15:
                lenth = 64
            last_data = response_data.iloc[0]['ADatas'] 
            
            receive_data = last_data[0:lenth]
        except:
            receive_data = []
            # logging.info('[0x{0:X}] no response'.format(receive_ID))

        readstr = binascii.b2a_hex(bytearray(receive_data),' ')
        logging.info('receive_ID = [{0:X}], receive_Data = [{1}]'.format(receive_ID,readstr))
        
        return receive_data
    
    '''擦除DID函数'''
    def EraseDID(self, Driver,iStep, area = 0):
        with allure.step(f'Step{iStep}:擦除DID'):
            logging.info("打开外部注入配置信息")
            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'profile 0',10)
            # 打开外部注入配置信息
            if area == 0:
                areaStr = 'DID'
            else:
                areaStr = 'Backup DID'
            respStr =  Driver['CAN2'].sendAndread_CMD_Shell(0x114,f'Test Erase {areaStr}',500)
            respStr = respStr.rstrip()
            time.sleep(0.5) 
            iStep += 1

        Driver['CAN2'].sendAndread_CMD_Shell(0x114,'cfg_mode 0',10)

        return iStep, respStr

    
    def SetEnduranceMode(self, Driver, iStep, Enable=False):
        if Enable:
            # 极限续航模式
            with allure.step(f"Step{iStep}: set Vehicle to Endurance Mode"):
                self.setSignalValue('VCU_0x48B', 'Ext_mode_cfg_self_study', 2, iStep)
                self.setSignalValue('VCU_0x343', 'Ext_mode_open_sts', 1, iStep)
                # S_343=Driver['CAN'].send_CANFrame_Cycle(0x343,[0x00,0x00,0x0,0x10,0x0,0x0,0x0,0x00],100) #打开极限续航
                # S_48B=Driver['CAN'].send_CANFrame_Cycle(0x48B,[0x04,0x00,0x0,0x00,0x0,0x0,0x10,0x00],100) #极限模式自主学习：10配置,01未配置
            logging.info('开启极限续航模式')
            Driver['CAN'].checkReqResp(Driver, [0x14,0xFF,0xFF,0xFF], [0x54], 0, iStep)
        else:  
            # 普通驾驶模式
            with allure.step(f"Step{iStep}: set Vehicle to General Driving mode"):
                self.setSignalValue('VCU_0x48B', 'Ext_mode_cfg_self_study', 0, iStep)
                self.setSignalValue('VCU_0x343', 'Ext_mode_open_sts', 0, iStep)
                # S_343=Driver['CAN'].send_CANFrame_Cycle(0x343,[0x00,0x00,0x0,0x00,0x0,0x0,0x0,0x00],100) #退出极限续航
                # S_48B=Driver['CAN'].send_CANFrame_Cycle(0x48B,[0x04,0x00,0x0,0x00,0x0,0x0,0x01,0x00],100) #退出极限续航
                # 退出极限续航3s后生成故障
                time.sleep(3)
            logging.info('退出极限续航模式')
        return iStep

    '''PCAN 重启指令'''
    def PcanRestart(self, Driver,iStep):
        with allure.step(f'Step{iStep}:PCAN发送指令重启设备'):
            logging.info("打开外部注入配置信息")
            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'profile 0',10)
            # 打开外部注入配置信息
            respStr =  Driver['CAN2'].sendAndread_CMD_Shell(0x114,f'cmd get 0x3000100 0 I 32',500)
            respStr = respStr.rstrip()
            time.sleep(0.5) 
            iStep += 1

        return iStep
    

    def GetTxMessageCount(self, Driver, iStep = 1, timeSleep = 1):
        MessageCount = []
        logging.info(f'清除缓存,并等待{timeSleep}s')
        Driver['CAN'].ClearBuffer()  # 清缓存  
        time.sleep(timeSleep)
        ALl_data,buffer_size = Driver['CAN'].read_frame()
        
        can_list = Set_up.txList.keys()
        if buffer_size != 0:
            for ID in can_list:
                with allure.step(f"Step{iStep}:Check ID =  {ID:X} count # Response: 0"):
                    response_data = ALl_data[ALl_data['AIdentifier'] == ID]
                    count =  len(response_data)
                    MessageCount.append(count)
                    iStep += 1
            if all(x >= 1 for x in MessageCount):
                logging.info(f'每个信号都接收到至少1个报文')
            if all(x == 0 for x in MessageCount):
                logging.info(f'每个信号都接收到0个报文')
            
        else:
            MessageCount.append(0)
            logging.info("No response data, 每个信号都接收到0个报文")

        return MessageCount, iStep
    
    def SetVehicleSpeed(self, Driver, iStep = 1, speed = 2):
        with allure.step(f"Step{iStep}:Set Vehicle Speed to {speed} # "):
            logging.info(f'设置车速为:{speed}')
            self.write_db_signal(Set_up.NetName,'VIU0', 'VIU_Route_Body3', 'IPB_VehicleSpeed2', speed)
            time.sleep(1)
        iStep += 1
        return iStep
    
    def SetVehicleSpeedStatus(self, Driver, iStep = 1, Enable = False):
        if Enable == False:
            with allure.step(f"Step{iStep}:set vehicle speed to invalid # NA"):
                logging.info(f'设置车速状态为:失效')
                self.write_db_signal(Set_up.NetName,'VIU0', 'VIU_Route_Body3', 'IPB_VehicleSpeedValid2', 0)
        else:
            with allure.step(f"Step{iStep}:set vehicle speed to valid # NA"):
                logging.info(f'设置车速状态为:有效')
                self.write_db_signal(Set_up.NetName,'VIU0', 'VIU_Route_Body3', 'IPB_VehicleSpeedValid2', 1)
        iStep += 1
        return iStep
    
    def Check_DTC_DTC_Status(self, DtcStatusValue):
        """
        检查DtcStatusValue的8个bit,每个bit代表不同的含义。
        
        Parameters:
            DtcStatusValue (int): 需要检查的状态值。
        
        Returns:
            dict: 包含每个bit状态及其含义的字典,以及最高优先级的故障信息。
        """
        bit_meanings = {
            0: "Test failed",
            1: "Test failed this operation cycle",
            2: "Pending DTC",
            3: "Confirmed DTC",
            4: "Test not completed since last clear",
            5: "Test failed since last clear",
            6: "Test not completed this operation cycle",
            7: "Warning indicator requested"
        }
        
        # 新增状态解析逻辑
        status = {}
        for bit, meaning in bit_meanings.items():
            status[meaning] = bool(DtcStatusValue & (1 << bit))
        
        # 判断最高优先级故障（根据UDS标准,Confirmed DTC具有最高优先级）
        priority_order = [
            "Test failed",            # 位0 最高优先级
            "Confirmed DTC",          # 位3 
            "Pending DTC",            # 位2
            "Test failed this operation cycle" # 位1
        ]
        
        highest_priority = None
        for fault in priority_order:
            if status.get(fault, False):
                highest_priority = fault
                break
        
        return {
            "status_bits": status,
            "highest_priority": highest_priority
        }
    
    def SetJiDianQiStatus(self, Driver, iStep = 1, iPort=6, Status = 0):
        strStatus = 'OFF' if Status == 0 else 'ON'
        with allure.step(f"Step{iStep}: set switch port{iPort} to {strStatus} # NA"):
            Driver['jidianqi'].SetPort(iPort, Status)
            logging.info(f'设置继电器{iPort}为{"OFF" if Status == 0 else "ON"}')  # 0=断开 1=闭合
        iStep += 1
        return iStep
    
    def recv_frame_by_id(self, Driver, ID, timeout = 3):
        """接收指定ID的CAN帧,带超时处理
        Args:
            Driver: 驱动对象
            ID: 要接收的CAN帧ID
            timeout: 超时时间(秒),默认3秒
        Returns:
            respData: 接收到的数据
        Raises:
            TimeoutError: 超时未收到指定ID的报文
        """
        Driver['CAN'].ClearBuffer()  # 清缓存
        start_time = time.time()
        
        while True:
            # 检查是否超时
            if time.time() - start_time > timeout:
                logging.error(f'超时未收到ID为0x{ID:X}的CAN报文')
                return []  # 超时返回None
            ALl_data, buffer_size = Driver['CAN'].read_frame()
            if buffer_size > 0:
                response_data = ALl_data[ALl_data['AIdentifier'] == ID]
                if not response_data.empty:
                    respData = response_data.iloc[0]['ADatas'][0:8]
                    actuRespStr = binascii.b2a_hex(bytearray(respData), ' ') 
                    logging.info('responseID:[0x{0:X}], Data:[{1}]'.format(ID, actuRespStr))
                    return respData
            time.sleep(0.1)

    '''接收后续所有连续帧'''
    def recv_frame_by_id_continuous(self, Driver, ID, frameCount = 1, timeout = 3):
        """接收指定ID的CAN帧,带超时处理
        Args:
            Driver: 驱动对象
            ID: 要接收的CAN帧ID
            frameCount: 要接收的CAN帧数量,默认1帧
            timeout: 超时时间(秒),默认3秒
        Returns:
            respData: 接收到的数据
            frameAtimeus: 接收到的时间戳
        Raises:
            TimeoutError: 超时未收到指定ID的报文
        """
        Driver['CAN'].ClearBuffer()  # 清缓存
        start_time = time.time()
        frameData = [] # 帧内容
        frameAtimeus = [] # 帧时间戳
        
        
        while True:
            # 检查是否超时
            if time.time() - start_time > timeout:
                logging.error(f'超时未收到ID为0x{ID:X}的CAN报文')
                return frameData, frameAtimeus  # 超时返回
            ALl_data, buffer_size = Driver['CAN'].read_frame()
            if buffer_size > 0:
                response_data = ALl_data[ALl_data['AIdentifier'] == ID]
                if not response_data.empty:
                    respData = response_data.iloc[0]['ADatas'][0:8]
                    actuRespStr = binascii.b2a_hex(bytearray(respData), ' ') 
                    logging.info('responseID:[0x{0:X}], Data:[{1}]'.format(ID, actuRespStr))
                    # 获取帧时间戳
                    ATimeUs = response_data.iloc[0]['ATimeUs']
                    frameAtimeus.append(ATimeUs)
                    frameData.append(respData)
                    if len(frameData) == frameCount:
                        return frameData, frameAtimeus

    def check_nm_state(self, Driver):
        """
        检查当前的NM状态,通过解析0x560报文获取状态信息
        Returns:
            dict: 包含以下状态信息的字典:
                - RMR_state: bool (第二字节第1位)
                - active_wake: bool (第二字节第5位) 
                - RMS_state: bool (第三字节第1位)
        """
        # 读取0x563报文
        nm_data = self.recv_frame_by_id(Driver, 0x563)
        if nm_data is not None: 
            readstr = binascii.b2a_hex(bytearray(nm_data),' ')
            logging.info(f"NM DATA : {readstr}")
        else:
            logging.info("read 3s,No NM data received")
        
        if not nm_data or len(nm_data) < 3:
            return {
                'RMR_state': None,
                'active_wake': None,
                'RMS_state': None,
                'error': 'Invalid NM frame data'
            }
        
        # 解析状态位
        byte1 = nm_data[1] if len(nm_data) > 1 else 0
        byte2 = nm_data[2] if len(nm_data) > 2 else 0
        
        return {
            'RMR_state': byte1 & 0x01,     # 第二字节第1位
            'active_wake': byte1 & 0x10,  # 第二字节第5位
            'RMS_state': byte2 & 0x01,    # 第三字节第1位
            'raw_data': nm_data
        }

    '''5.8G读取OBU版本号'''
    def getOBUSoftwareVersion(self, Driver, iStep):
        '''
        读取OBU版本号 【 ApduData 】 : 【 AA5506020E8C040280】
        '''
        test_str = ''
        with allure.step(f'Step{iStep}: 读取OBU版本号 # '):
            logging.info(f'读取OBU版本号')
            test_str = Driver['M600'].SendBST()  # 发送BST
            if test_str == None:
                logging.info(f'发送BST失败')
                return iStep, ""
            apdu1 = 'AA 55 06 02 0E 8C 04 02 80'
            SoftwareVersion = Driver['M600'].TransferChannel(channelID='BB', Apdus=apdu1)  # 传输通道
            test_str = Driver['M600'].SetMMI()  # 设置MMI.
            Driver['M600'].EventReport()
            iStep += 1

        return iStep, SoftwareVersion

    '''5.8G设置OBU蓝牙名称'''
    def setOBUBleName(self, Driver, iStep, bleName):
        '''
        设置OBU蓝牙名称 【 ApduData 】 : 【 0F8800】
        '''
        # Ascii码转换为16进制字符串
        hex_str = '00' + ''.join(format(ord(char), '02x') for char in bleName) + '00'
        test_str = ''
        with allure.step(f'Step{iStep}: 设置OBU蓝牙名称 # {bleName}'):
            logging.info(f'设置OBU蓝牙名称为{bleName}')
            # Driver['M600'].InitRSU(channel_id='01')  # 初始化RSU
            test_str = Driver['M600'].SendBST()  # 发送BST
            if test_str == None:
                logging.info(f'发送BST失败')
                return iStep, ""
            # test_str = Driver['M600'].GetSecure()  # 获取Secure
            apdu1 = f'AA 55 06 {len(hex_str)//2:02X} 0F 88 00 {hex_str}'
            apdu1 += f'{self.getbcc(apdu1):02X}'
            bleName = Driver['M600'].TransferChannel(channelID='BB', Apdus=apdu1)  # 传输通道
            test_str = Driver['M600'].SetMMI()  # 设置MMI.
            Driver['M600'].EventReport()
            iStep += 1

        return iStep, bleName
    
    '''5.8G读取OBU蓝牙名称'''
    def getOBUBleName(self, Driver, iStep):
        '''
        读取OBU蓝牙名称 【 ApduData 】 : 【 AA5506020E88000280】
        '''
        test_str = ''
        with allure.step(f'Step{iStep}: 读取OBU蓝牙名称 # '):
            logging.info(f'读取OBU蓝牙名称')
            # Driver['M600'].InitRSU(channel_id='01')  # 初始化RSU
            test_str = Driver['M600'].SendBST()  # 发送BST
            if test_str == None:
                logging.info(f'发送BST失败')
                return iStep, ""
            # test_str = Driver['M600'].GetSecure()  # 获取Secure
            apdu1 = 'AA 55 06 02 0E 88 00 02 80'
            bleName = Driver['M600'].TransferChannel(channelID='BB', Apdus=apdu1)  # 传输通道
            test_str = Driver['M600'].SetMMI()  # 设置MMI.
            Driver['M600'].EventReport()
            iStep += 1

        return iStep, bleName

    '''5.8G设置OBU ID与SN'''
    def setOBU_ID_SN(self, Driver, iStep, OBU_ID, OBU_SN):
        '''
        设置OBU ID与SN 【 ApduData 】 : 【 0F8100】
        '''
        # Ascii码转换为16进制字符串
        hex_str = ''.join(format(ord(char), '02x') for char in OBU_SN)
        test_str = ''
        with allure.step(f'Step{iStep}: 设置OBU ID与SN # {OBU_ID} {OBU_SN}'):
            logging.info(f'设置OBU ID与SN为{OBU_ID} {OBU_SN}')
            # Driver['M600'].InitRSU(channel_id='01')  # 初始化RSU
            test_str = Driver['M600'].SendBST()  # 发送BST
            if test_str == None:
                logging.info(f'发送BST失败')
                return iStep, ""
            # test_str = Driver['M600'].GetSecure()  # 获取Secure
            apdu1 = f'AA 55 06 16 0F 81 00 02 {OBU_ID} {hex_str}'
            apdu1 += f'{self.getbcc(apdu1):02X}'
            test_str = Driver['M600'].TransferChannel(channelID='BB', Apdus=apdu1)  # 传输通道
            test_str = Driver['M600'].SetMMI()  # 设置MMI.
            Driver['M600'].EventReport()
            iStep += 1

        return iStep
    
    '''5.8G读取OBU ID与SN'''
    def getOBU_ID_SN(self, Driver, iStep):
        '''
        读取OBU ID与SN 【 ApduData 】 : 【 AA5506020E810002】
        '''
        test_str = ''
        with allure.step(f'Step{iStep}: 读取OBU ID与SN # '):
            logging.info(f'读取OBU ID与SN')
            # Driver['M600'].InitRSU(channel_id='01')  # 初始化RSU
            test_str = Driver['M600'].SendBST()  # 发送BST
            if test_str == None:
                logging.info(f'发送BST失败')
                return iStep, ""
            # test_str = Driver['M600'].GetSecure()  # 获取Secure
            apdu1 = 'AA 55 06 02 0E 81 00 02 80'
            ID_SN = Driver['M600'].TransferChannel(channelID='BB', Apdus=apdu1)  # 传输通道
            test_str = Driver['M600'].SetMMI()  # 设置MMI.
            Driver['M600'].EventReport()
            iStep += 1

        return iStep, ID_SN

    # CAN信号读取SN
    def getOBU_SN(self, Driver, iStep):
        '''
        从CAN信号读取OBU SN
        '''
        test_str = ''
        with allure.step(f'Step{iStep}: 从CAN信号读取OBU SN # '):
            logging.info(f'从CAN信号读取OBU SN')
            SN = self.recv_frame_by_id(Driver, 0x5E9, 2)
            sn_Str = binascii.hexlify(bytearray(SN)).decode('utf-8')
            iStep += 1
        return iStep, sn_Str

    '''5.8G设置OBU通行状态'''
    def SetOBUTradeInfo(self, Driver, iStep, value):
        '''
        设置OBU通行状态
        0x00: 正常
        0x01: 异常
        0x02: 联系运行商
        0x03: 无卡
        ''' 
        ETC_Trade_Info = {
            0: "通行正常",
            1: "通行异常",
            2: "联系运行商",
            3: "无卡"
        }
        status_desc = ETC_Trade_Info.get(value, "Unknown status")
        with allure.step(f'Step{iStep}: 设置OBU交易状态为: {status_desc} # '):
            logging.info(f'设置OBU交易状态为: {status_desc}')
            test_str = Driver['M600'].SendBST()  # 发送BST
            if test_str == None:
                logging.info(f'发送BST失败')
                return iStep
            test_str = Driver['M600'].SetMMI(Paramter = str(value).zfill(2))  # 设置MMI
            Driver['M600'].EventReport()
            iStep += 1

        return iStep

    '''5.8G设置OBU交易金额与出入口状态'''
    def SetOBUTradeMoney(self, Driver, iStep, iMoney = 0, Entrance_Exit_Status = 2, Paramter = 0):
        '''
        设置OBU交易金额与出入口状态
        iMoney: 交易金额(分)
        Entrance_Exit_Status: 出入口状态
        '''
        ETC_Trade_Info = {
            0: "",
            1: "封闭MTC入口",
            2: "封闭MTC出口",
            3: "封闭ETC入口",
            4: "封闭ETC出口",
            5: "MTC开放式入口",
            6: "MTC开放式出口",
        }
        status_desc = ETC_Trade_Info.get(Entrance_Exit_Status, "Unknown status")
        with allure.step(f'Step{iStep}: 设置OBU交易金额与出入口状态: {iMoney}分 {status_desc} # NA'):
            logging.info(f'设置OBU交易金额与出入口状态: {iMoney}分 {Entrance_Exit_Status}:{status_desc}')
            TradeStatus = Driver['M600'].etc_transaction(iMoney = iMoney, Entrance_Exit_Status = Entrance_Exit_Status, Paramter= str(Paramter).zfill(2))
            iStep += 1
            
        ETC_Trade_Info = {
            0: "通行正常",
            1: "通行异常",
            2: "联系运行商",
            3: "无卡"
        }
        status_desc = ETC_Trade_Info.get(Paramter, "Unknown status")
        with allure.step(f'Step{iStep}: 设置OBU交易状态为: {status_desc} # NA'):
            logging.info(f'设置OBU交易状态为: {status_desc}')
            iStep += 1
        return iStep, TradeStatus

    '''5.8G设置OBU防拆状态'''
    def setOBUTampersSt(self, Driver, iStep, value = 0):
        '''
        设置OBU防拆认证状态
        0: 已拆卸
        1: 未拆卸
        '''

        OBU_DISMOUNT_STATUS = {
            0: "已拆卸",
            1: "未拆卸",
        }
        status_desc = OBU_DISMOUNT_STATUS.get(value, "Unknown status")
        with allure.step(f'Step{iStep}: 设置OBU防拆认证状态为: {status_desc} # '):
            logging.info(f'设置OBU防拆认证状态为: {status_desc}')
            status = Driver['M600'].write_obu_tamper(tamper_status = value)
        return iStep, status

    '''5.8G获取OBU防拆状态'''
    def getOBUTampersSt(self, Driver, iStep):
        '''
        5.8G 获取OBU防拆认证状态
        '''
        with allure.step(f'Step{iStep}: 获取OBU防拆认证状态 # '):
            status = Driver['M600'].read_obu_tamper()
        return iStep, status 

    '''CAN 发送VIN'''
    def update_VIN(self, iStep):
        '''CAN 发送VIN'''
        send_ID = 0x330
        newVIN = "22345673901234467"
        VIN_bytes = string_to_vin(newVIN)
        send_data = [
            [0x01] + VIN_bytes[0:7],
            [0x02] + VIN_bytes[7:14],
            [0x03] + VIN_bytes[14:17] + [0x00] * 4
        ]
        with allure.step(f'Step{iStep}: 发送ID=0x{send_ID:03X}的ZCLZCLCan8Frame43信号更新VIN # 更新VIN = {newVIN}'):
            logging.info(f'发送DhuInfoCanFr91信号,更新VIN为{newVIN}')
            for j in range(3):
                for i in range(3):
                    self.TransmitData(send_ID, send_data[i], Set_up.CAN_Type)
                    time.sleep(0.5)
            iStep += 1
        return iStep

    '''5.8G 读取OBU VIN'''
    def getOBUVIN(self, Driver, iStep):
        '''
        5.8G 读取OBU VIN AA5504040EB00402F190D9
        '''
        test_str = ''
        with allure.step(f'Step{iStep}: 读取OBU VIN # '):
            test_str = Driver['M600'].SendBST()  # 发送BST
            if test_str == None:
                logging.info(f'发送BST失败')
                return iStep, ""
            apdu1 = 'AA5504040EB00402F190D9'
            response = Driver['M600'].TransferChannel(channelID='BB', Apdus=apdu1)  # 传输通道
            try:
                VIN = response.replace(" ", "")[-40:-6]
            except:
                VIN = '00' * 39
            test_str = Driver['M600'].SetMMI()  # 设置MMI.
            Driver['M600'].EventReport()
            iStep += 1
            VIN_bytes = self.strToToHexBytes(VIN)
            VIN = ''.join([hex(b)[2:].zfill(2) for b in VIN_bytes])
            logging.info(f'读取OBU VIN : {VIN}')

        return iStep, VIN_bytes

    '''CAN 读取ETC激活状态'''
    def getETC_ActiveStatus(self, iStep):
        '''
        读取ETC激活状态
        '''
        ETC_ActvSts_info = {
            0: "Success",
            1: "Fail",
            2: "Reserved",
            3: "Invalid"
        }
        with allure.step(f'Step{iStep}: 读取ETC激活状态 # '):
            ETC_ActvSts = self.read_db_signal(Set_up.NetName,'ETC', 'ETC_Status', 'ETC_ActvSts')
            status_desc = ETC_ActvSts_info.get(ETC_ActvSts, "Unknown status")
            logging.info(f'读取ETC激活状态: {ETC_ActvSts}({status_desc})')
            iStep += 1
        return iStep, ETC_ActvSts
    
    '''CAN 读取ETC防拆认证结果'''
    def getETC_AntiDismantleAuthentRes(self, iStep):
        '''
        读取ETC防拆认证结果
        '''
        ETC_AntiDismantleAuthentRes_info = {
            0: "Success",
            1: "Fail",
            2: "Reserved",
            3: "Invalid"
        }
        with allure.step(f'Step{iStep}: 读取ETC防拆认证结果 # '):
            ETC_AntiDismantleAuthentRes = self.read_db_signal(Set_up.NetName,'ETC', 'ETC_Status', 'ETC_AntiDismantleAuthentRes')
            status_desc = ETC_AntiDismantleAuthentRes_info.get(ETC_AntiDismantleAuthentRes, "Unknown status")
            logging.info(f'读取ETC防拆认证结果: {ETC_AntiDismantleAuthentRes}({status_desc})')
            iStep += 1
        return iStep, ETC_AntiDismantleAuthentRes

    '''CAN 读取蓝牙自检状态'''
    def getBLE_SelfCheckStatus(self, iStep):
        '''
        读取蓝牙自检状态
        '''
        BluetoothSelfCheckSts_info = {
            0: "Success",
            1: "Fail",
            2: "Reserved",
            3: "Invalid"
        }
        with allure.step(f'Step{iStep}: 读取蓝牙自检状态 # '):
            ETC_SelfChk_BLE = self.read_db_signal(Set_up.NetName,'ETC', 'ETC_Status', 'ETC_SelfChk_BLE')
            status_desc = BluetoothSelfCheckSts_info.get(ETC_SelfChk_BLE, "Unknown status")
            logging.info(f'读取蓝牙自检状态: {ETC_SelfChk_BLE}({status_desc})')
            iStep += 1
        return iStep, ETC_SelfChk_BLE

    '''CAN 读取ESAM自检状态'''
    def getESAM_SelfCheckStatus(self, iStep):
        '''
        读取ESAM自检状态
        '''
        ESAMSelfCheckSts_info = {
            0: "Success",
            1: "Fail",
            2: "Reserved",
            3: "Invalid"    
        }
        with allure.step(f'Step{iStep}: 读取ESAM自检状态 # '):
            ETC_SelfChk_ESAM = self.read_db_signal(Set_up.NetName,'ETC', 'ETC_Status', 'ETC_SelfChk_ESAM')
            status_desc = ESAMSelfCheckSts_info.get(ETC_SelfChk_ESAM, "Unknown status")
            logging.info(f'读取ESAM自检状态: {ETC_SelfChk_ESAM}({status_desc})')
            iStep += 1
        return iStep, ETC_SelfChk_ESAM
    
    '''CAN 读取ETC总自检状态'''
    def getETC_SelfCheckStatus(self, iStep):
        '''
        读取ETC总自检状态
        '''
        ETCSelfCheckSts_info = {
            0: "Success",
            1: "Fail",
            2: "Reserved",
            3: "Invalid"
        }
        with allure.step(f'Step{iStep}: 读取ETC总自检状态 # '):
            ETC_SelfChkSts = self.read_db_signal(Set_up.NetName,'ETC', 'ETC_Status', 'ETC_SelfChkSts')
            status_desc = ETCSelfCheckSts_info.get(ETC_SelfChkSts, "Unknown status")
            logging.info(f'读取ETC总自检状态: {ETC_SelfChkSts}({status_desc})')    
            iStep += 1
        return iStep, ETC_SelfChkSts

    '''CAN 读取ETC交易状态'''
    def getETC_TradeStatus(self, iStep):
        '''
        读取ETC交易状态
        '''
        ETC_TradeStatus_info = {
            0: "Success",
            1: "Abnormal",
            2: "Contact_the_Service_Provider",
            3: "NoCard",
            4: "Reserved",
        }
        with allure.step(f'Step{iStep}: 读取ETC交易状态 # '):
            ETC_TradeStatus = int(self.read_db_signal(Set_up.NetName,'ETC', 'ETC_Amount', 'ETC_TransactionSts'))
            status_desc = ETC_TradeStatus_info.get(ETC_TradeStatus, "Unknown status")
            logging.info(f'读取ETC交易状态: {ETC_TradeStatus}({status_desc})')
            iStep += 1
        return iStep, ETC_TradeStatus

    '''CAN 读取ETC交易金额'''
    def getETC_TradeAmount(self, iStep):
        '''
        读取ETC交易金额
        '''
        with allure.step(f'Step{iStep}: 读取ETC交易金额 # '):
            ETC_TransactionAmnt = self.read_db_signal(Set_up.NetName,'ETC', 'ETC_Amount', 'ETC_TransactionAmnt')
            logging.info(f'读取ETC交易金额: {ETC_TransactionAmnt}')
            iStep += 1
        return iStep, ETC_TransactionAmnt

    '''CAN 读取ETC交易入口状态'''
    def getETC_TradeEntranceStatus(self, iStep):
        '''
        读取ETC交易入口状态
        '''
        ETC_TradeEntranceStatus_info = {
            0: "Reserved",
            1: "Enclosed_Manual_Toll_Collection_Entrance",
            2: "Enclosed_Manual_Toll_Collection_Exit",
            3: "Enclosed_Electronic_Toll_Collection_Entrance",
            4: "Enclosed_Electronic_Toll_Collection_Exit",
            5: "Open_ended_Manual_Toll_Collection",
            6: "Open_ended_Electronic_Toll_Collection",
            7: "Reserved",
            8: "Reserved",
            9: "Reserved",
            10: "Reserved",
        }   
        with allure.step(f'Step{iStep}: 读取ETC交易入口状态 # '):
            ETC_ExpresswayEntrcAndExitSts = self.read_db_signal(Set_up.NetName,'ETC', 'ETC_Amount', 'ETC_ExpresswayEntrcAndExitSts')
            status_desc = ETC_TradeEntranceStatus_info.get(ETC_ExpresswayEntrcAndExitSts, "Unknown status")
            logging.info(f'读取ETC交易入口状态: {ETC_ExpresswayEntrcAndExitSts}({status_desc})')
            iStep += 1
        return iStep, ETC_ExpresswayEntrcAndExitSts 

    '''5.8G 写入数据块'''
    def setData(self, Driver, iStep, Did, Data):
        '''
        写入数据块
        '''
        test_str = ''
        data_str = Data.replace(" ", "")
        ascii_data = bytes.fromhex(data_str).decode('ascii')
        with allure.step(f'Step{iStep}: 设置数据块 {Did} # {ascii_data}'):
            
            logging.info(f'设置数据块{Did}为{ascii_data}')
            test_str = Driver['M600'].SendBST()  # 发送BST
            if test_str == None:
                logging.info(f'发送BST失败')
                return iStep, ""

            apdu1 = f'AA5502{len(data_str)//2 + 4:02X}0FB00402 {Did} {data_str}'
            apdu1 += f'{self.getbcc(apdu1):02X}'
            test_str = Driver['M600'].TransferChannel(channelID='BB', Apdus=apdu1)  # 传输通道
            test_str = Driver['M600'].SetMMI()  # 设置MMI.
            Driver['M600'].EventReport()
            iStep += 1
        return iStep
    
    '''计算bcc'''
    def getbcc(self, apdu):
        '''
        计算bcc
        '''
        bcc = 0
        for i in range(0, len(apdu), 2):
            bcc ^= int(apdu[i:i+2], 16)
        return bcc

    '''5.8G软件升级'''
    def upgrade_OBU(self, Driver, iStep):
        '''
        5.8G软件升级
        '''
        with allure.step(f'Step{iStep}: 5.8G软件升级 # NA'):
            updateStatus = Driver['M600'].update_version(Set_up.bin_path, Set_up.bin_Offset)
            iStep += 1
            if updateStatus == False:
                logging.info(f'5.8G软件升级失败')
                return iStep, '', False
        with allure.step(f'Step{iStep}: 获取升级后的软件版本 # NA'):
            time.sleep(10)
            version = Driver['M600'].read_version()
            iStep += 1
        return iStep, version, True

# run main
if __name__ == "__main__":
    import os
    root = os.getcwd()
    project_DBC_File_CAN = os.path.join(root, r'data\MRR_20220901.dbc')
    logging.info(project_DBC_File_CAN)
    try:


        rbs = real_CAN_TC(drive_type = 'USE_TS_TC1011_DEVICE',    # USE_TS_TC1014_DEVICE /   USE_TS_VIRTUAL_DEVICE
                    configure_baudrate = {'CAN_Type':'CANFD','ArbRate':500,'DataRate':2000,'TermResistor':True}
                    )
        rbs.load_dbc(project_DBC_File_CAN)
        # rbs.register_event_can_T_R(OnCANevent)
        # rbs.register_event_canfd_T_R(OnCANFDevent)
        rbs.set_0x222_rolling_counter_error(enable=False)
        # rbs.register_event_canfd_T_pre(OnCANPreFDevent)
        
        rbs.connect_application()
        rbs.RBS_Config('Vehicle_CAN',['BCM'])

        logging.info('send frame')
        rbs.TransmitData(0x666,[0x61,2,3,4,5,6,7,8,
                              0x22,2,3,4,5,6,7,8,
                              0x23,2,3,4,5,6,7,8,
                              0x24,2,3,4,5,6,7,8,
                              0x25,2,3,4,5,6,7,8,
                              0x26,2,3,4,5,6,7,8,
                              0x27,2,3,4,5,6,7,8,
                              0x28,2,3,4,5,6,7,8],'CANFD')
        rbs.TransmitData(0x777,[0x71,2,3,4,5,6,7,8,],'CAN')
        rbs.TransmitData(0x777,[0x81,2,3,4,5,6,7,8,],'CAN')
        rbs.TransmitData(0x444,[0x41,2,3,4,5,6,7,8,],'CAN')

        a =rbs.send_CANFrame_Cycle(0x22,[0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88],100)
        
        logging.info('step1_wait_1000_:::::')
        rbs.ClearBuffer()
        rbs.TS_delay(1000)
        rbs.read_frame()

        logging.info('step2__wait_2000_:::::') 
        rbs.ClearBuffer()
        rbs.TS_delay(2000)
        rbs.read_frame()

        logging.info('step2__wait_2000_:::::') 
        rbs.ClearBuffer()
        rbs.TS_delay(2000)
        rbs.read_frame()

        logging.info('step3__wait_3000_:::::') 
        rbs.ClearBuffer()
        rbs.TS_delay(3000)
        rbs.read_frame()

        ttttt = rbs.sendAndread_CMD_Shell(0x114,'test 12',500)
        ttttt = rbs.sendAndread_CMD_Shell(0x114,'test 12',500)
        logging.info(ttttt)
        rbs.stop_FrANFrame_Cycle(a)

        rbs.write_db_signal('SCAN4','GW','ABS1','ABS1_FLWheelSpd',34.0)

        aa = rbs.read_db_signal('SCAN4','GW','ABS1','ABS1_FLWheelSpd')
        logging.info('read rbs signal value is ',aa)

        # diag = TSCAN_UDS()
        # diag.req_and_res_can([0x22,0xf1,0x90],3)
        # diag.UDS_can_delete()

        # obj = HIL_Obj(rbs)
        # obj.setDaemon(True)
        # obj.open()
        # obj.obj_sent()
        # obj.start()
        # obj.last_warninh_data
        # obj.warning_data

        rbs.dis_connect_application()

        logging.info('Over All')
        import sys
        sys.exit(0)

    except Exception as e :
        rbs.dis_connect_application()
        import sys
        import traceback
        logging.info(traceback.print_exc())
        sys.exit(0)
        pass
    
    except KeyboardInterrupt :
        rbs.dis_connect_application()
        import sys
        import traceback
        logging.info(traceback.print_exc())
        sys.exit(0)
        pass
